﻿using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenAuth.AspNet;
using FluentValidation.Results;
using log4net;
using Microsoft.Web.WebPages.OAuth;
using QAS.Nekorama.Filters;
using QAS.Nekorama.Models;
using WebMatrix.WebData;

namespace QAS.Nekorama.Controllers
{
    /// <summary>
    /// アカウントコントローラー
    /// </summary>
    [Authorize]
    [InitializeSimpleMembership]
    [HandleError]
    public class AccountController : Controller
    {
        private readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        /// <summary>
        /// ログオフアクション 
        /// </summary>
        /// <returns>Question Index ビュー</returns>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            _log.Debug(string.Format("Log out userid {0}", WebSecurity.CurrentUserId));

            WebSecurity.Logout();
            _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);

            return RedirectToAction("Index", "Question");
        }

        //
        // POST: /Account/ExternalLogin

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLogin(string provider, string returnUrl)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
            return new ExternalLoginResult(provider,
                Url.Action("ExternalLoginCallback", new {ReturnUrl = Request.UrlReferrer.AbsolutePath}));
        }

        //
        // GET: /Account/ExternalLoginCallback

        [AllowAnonymous]
        public ActionResult ExternalLoginCallback(string returnUrl)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            AuthenticationResult result =
                OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new {ReturnUrl = returnUrl}));
            if (!result.IsSuccessful)
            {
                _log.Error(string.Format("Login failed."));
                return RedirectToAction("ExternalLoginFailure");
            }

            if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, false))
            {
                _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                return RedirectToLocal(returnUrl);
            }

            if (User.Identity.IsAuthenticated)
            {
                // 今のユーザーはググルでログインしましたから、新しいアカウントを作ります。
                OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
                _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                return RedirectToLocal(returnUrl);
            }
            //ユーザーは新しくて、 欲しがる表示名の入力を要求します。
            string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
            ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
            ViewBag.ReturnUrl = returnUrl;
            _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
            return View("ExternalLoginConfirmation",
                new RegisterExternalLoginModel
                {
                    DisplayName = result.UserName,
                    ExternalLoginData = loginData,
                    Email = result.UserName
                });
        }

        //
        // POST: /Account/ExternalLoginConfirmation

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public ActionResult ExternalLoginConfirmation(RegisterExternalLoginModel model, string returnUrl)
        {
            _log.Debug("Start action " + MethodBase.GetCurrentMethod().Name);
            if (string.IsNullOrEmpty(model.DisplayName))
            {
                model.DisplayName = "";
            }
            model.DisplayName = model.DisplayName.Trim();
            string provider = null;
            string providerUserId = null;

            OAuthWebSecurity.TryDeserializeProviderUserId(model.ExternalLoginData, out provider, out providerUserId)
                ;

            var validator = new CustomValidators.ExternalLoginValidator();
            ValidationResult result = validator.Validate(model);
            if (result.IsValid)
            {
                // 新しいユーザーをデータベースに保存します。
                using (var db = new Entities())
                {
                    User user = db.Users.FirstOrDefault(u => u.DisplayName.ToLower() == model.DisplayName.ToLower());
                    var r = new Random();
                    // ユーザーが存在したかどうか確認します。
                    if (user == null)
                    {                    
                        db.Users.Add(new User
                        {
                            DisplayName = model.DisplayName.Trim(),
                            FullName = model.DisplayName,
                            Email = model.Email,
                            Avatar = r.Next(1,15)+".jpg",
                            Reputation = 1,
                            Location = RegionInfo.CurrentRegion.DisplayName,
                            About = "",
                            BanDuration = 0,
                            CreateDate = DateTime.Now,
                            CreatedUser = 0
                        });

                        db.SaveChanges();

                        OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.DisplayName);
                        OAuthWebSecurity.Login(provider, providerUserId, false);
                        _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
                        return RedirectToLocal(returnUrl);
                    }
                    
                }
            }
            ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(provider).DisplayName;
            ViewBag.ReturnUrl = returnUrl;
            _log.Debug("End action " + MethodBase.GetCurrentMethod().Name);
            return View(model);
        }

        // GET: /Account/ExternalLoginFailure

        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

        #region Helpers

        public enum ManageMessageId
        {
            ChangePasswordSuccess,
            SetPasswordSuccess,
            RemoveLoginSuccess,
        }

        private ActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            return RedirectToAction("Index", "Question");
        }

        private static string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            switch (createStatus)
            {
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";

                case MembershipCreateStatus.DuplicateEmail:
                    return
                        "A user name for that e-mail address already exists. Please enter a different e-mail address.";

                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";

                case MembershipCreateStatus.InvalidEmail:
                    return "The e-mail address provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";

                case MembershipCreateStatus.ProviderError:
                    return
                        "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                case MembershipCreateStatus.UserRejected:
                    return
                        "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

                default:
                    return
                        "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
            }
        }

        internal class ExternalLoginResult : ActionResult
        {
            public ExternalLoginResult(string provider, string returnUrl)
            {
                Provider = provider;
                ReturnUrl = returnUrl;
            }

            public string Provider { get; private set; }

            public string ReturnUrl { get; private set; }

            public override void ExecuteResult(ControllerContext context)
            {
                OAuthWebSecurity.RequestAuthentication(Provider, ReturnUrl);
            }
        }

        #endregion Helpers
    }
}